package main

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"

	"go-file-perception-model/internal/config"
	"go-file-perception-model/internal/model"
	"go-file-perception-model/internal/service"
	"go-file-perception-model/pkg/embedding"
	"go-file-perception-model/pkg/vector"
)

// Tool descriptor for tools/list
func ToolSemanticSearchDescriptor() Tool {
	return Tool{
		Name:        "semantic_search",
		Description: "语义搜索, 基于向量与嵌入模型的语义搜索，默认启用结果文件去重归纳；支持按文件类型与目录过滤。返回结果包含两个字段：'content'数组包含搜索结果项，每个结果项包含文件路径、文件名、匹配得分、匹配位置等信息；'metadata'对象包含搜索元数据，如结果总数、是否启用去重归纳、查询文本和相似度阈值等。当启用去重归纳时，结果项中的content字段会显示匹配次数和相似度最高的片段内容；如果content字段为空，表示只返回了文件的元数据信息，不包含具体内容片段。",
		InputSchema: map[string]any{
			"type": "object",
			"properties": map[string]any{
				"query":            map[string]any{"type": "string", "description": "查询文本(必填)"},
				"limit":            map[string]any{"type": "integer", "default": 10, "description": "返回条数(去重后)"},
				"threshold":        map[string]any{"type": "number", "default": 0.0, "description": "相似度阈值"},
				"file_types":       map[string]any{"type": "array", "items": map[string]any{"type": "string"}},
				"directories":      map[string]any{"type": "array", "items": map[string]any{"type": "string"}},
				"enable_dedup":     map[string]any{"type": "boolean", "default": true, "description": "结果文件去重归纳(默认启用),启用该参数时候，返回结果每个文件只会出现一次，并在content中描述该文件匹配到的次数以及相似度最高的片段内容"},
				"dedup_multiplier": map[string]any{"type": "integer", "default": 100, "description": "去重归纳时扩大检索倍数(用于先广搜再归纳)"},
			},
			"required": []string{"query"},
		},
	}
}

// Non-stream call handler
func CallSemanticSearch(args map[string]any) (any, error) {
	query, _ := args["query"].(string)
	if query == "" {
		return nil, fmt.Errorf("query required")
	}
	limit := 10
	if v, ok := args["limit"].(float64); ok {
		limit = int(v)
	}
	threshold := float32(0)
	if v, ok := args["threshold"].(float64); ok {
		threshold = float32(v)
	}
	fileTypes := extractStringArray(args["file_types"])
	dirs := extractStringArray(args["directories"])
	enableDedup := true
	if v, ok := args["enable_dedup"].(bool); ok {
		enableDedup = v
	}
	dedupMul := 100
	if v, ok := args["dedup_multiplier"].(float64); ok {
		dedupMul = int(v)
	}

	// Build dependencies from config
	cfg, err := config.LoadConfig()
	if err != nil {
		return nil, fmt.Errorf("failed to load config: %w", err)
	}

	// Initialize vector database
	vdb, err := vector.NewQdrantDBWithAPIKey(cfg.VectorDB.Host, cfg.VectorDB.GRPCPort, cfg.VectorDB.APIKey)
	if err != nil {
		return nil, fmt.Errorf("failed to initialize vector database: %w", err)
	}

	// Initialize embedding model
	bgeModel, err := embedding.NewBGEModel(&embedding.BGEServiceConfig{
		Host:    cfg.BGEService.Host,
		Port:    cfg.BGEService.Port,
		APIKey:  cfg.BGEService.APIKey,
		Timeout: cfg.BGEService.Timeout,
	})
	if err != nil {
		return nil, fmt.Errorf("failed to initialize embedding model: %w", err)
	}

	// Open file monitor repository
	repo, err := openFileMonitorRepo()
	if err != nil {
		return nil, fmt.Errorf("failed to open file monitor repository: %w", err)
	}
	defer repo.Close()

	// Create semantic search service
	sem := service.NewSemanticSearchService(vdb, bgeModel, repo)

	// Resolve directories relative to index dirs if provided
	resolvedDirs, err := resolveDirectories(dirs)
	if err != nil {
		return nil, fmt.Errorf("failed to resolve directories: %w", err)
	}

	// Determine Qdrant collection name from SQLite by directory path
	var collectionName string
	var collectionFound bool

	// Prefer the first resolved directory that has a record
	for _, d := range resolvedDirs {
		if rec, e := repo.GetDirectoryRecordByPath(d); e == nil && rec != nil && rec.CollectionName != "" {
			collectionName = rec.CollectionName
			collectionFound = true
			break
		}
	}

	// If no collection found from resolved directories, try to get any available collection
	if !collectionFound {
		if records, e := repo.ListAllDirectoryRecords(); e == nil {
			for _, r := range records {
				if r != nil && r.CollectionName != "" {
					collectionName = r.CollectionName
					collectionFound = true
					break
				}
			}
		}
	}

	// If still no collection found, use default collection name
	if !collectionFound {
		collectionName = "file_chunks"
	}

	// Set the collection name for vector database
	vdb.SetCollection(collectionName)

	req := model.SemanticSearchRequest{
		SearchRequest: model.SearchRequest{
			Query:           query,
			Limit:           limit,
			Threshold:       threshold,
			FileTypes:       fileTypes,
			Directories:     resolvedDirs,
			EnableDedup:     enableDedup,
			DedupMultiplier: dedupMul,
		},
		TopK: limit,
	}
	results, err := sem.Search(req)
	if err != nil {
		return nil, err
	}

	// Normalize file paths to relative to first index dir
	var indexDir string
	if len(cfg.FileIndex.IndexDirectories) > 0 {
		indexDir = cfg.FileIndex.IndexDirectories[0]
	}
	
	// 处理结果，将每个结果包装为符合MCP标准的内容对象
	var processedResults []map[string]any
	for _, r := range results {
		if indexDir != "" {
			r.FilePath = getRelativePath(r.FilePath, indexDir)
		}
		
		// 创建一个符合MCP标准的内容对象
		contentObj := map[string]any{
			"type": "text",
		}
		
		// 构建文本内容，包含所有相关信息
		textContent := fmt.Sprintf("文件: %s\n路径: %s\n类型: %s\n大小: %d字节\n匹配得分: %.4f\n",
			r.FileName, r.FilePath, r.FileType, r.FileSize, r.Score)
		
		// 添加位置信息
		if r.StartLine > 0 && r.EndLine > 0 {
			textContent += fmt.Sprintf("位置: 第%d-%d行\n", r.StartLine, r.EndLine)
		}
		
		// 添加内容片段
		if r.Content != "" {
			textContent += fmt.Sprintf("\n内容片段:\n%s\n", r.Content)
		}
		
		// 添加高亮内容
		if r.Highlight != "" {
			textContent += fmt.Sprintf("\n高亮内容:\n%s\n", r.Highlight)
		}
		
		contentObj["text"] = textContent
		
		// 添加元数据
		metadata := map[string]any{
			"chunk_id":    r.ChunkID,
			"score":       r.Score,
			"chunk_no":    r.ChunkNo,
			"start_pos":   r.StartPos,
			"end_pos":     r.EndPos,
			"start_line":  r.StartLine,
			"end_line":    r.EndLine,
			"file_id":     r.FileID,
			"file_path":   r.FilePath,
			"file_name":   r.FileName,
			"file_type":   r.FileType,
			"file_size":   r.FileSize,
			"indexed_at":  r.IndexedAt,
		}
		
		// 如果有内容片段，也添加到元数据中
		if r.Content != "" {
			metadata["content"] = r.Content
		}
		
		// 如果有高亮内容，也添加到元数据中
		if r.Highlight != "" {
			metadata["highlight"] = r.Highlight
		}
		
		contentObj["metadata"] = metadata
		processedResults = append(processedResults, contentObj)
	}
	return map[string]any{
		"content": processedResults, 
		"metadata": map[string]any{
			"total": len(processedResults), 
			"enable_dedup": enableDedup,
			"query": query,
			"threshold": threshold,
		},
	}, nil
}

// Stream call handler - emit one item per final (possibly deduped) result
func StreamSemanticSearch(ctx context.Context, args map[string]any, enc *json.Encoder, flusher http.Flusher) error {
	res, err := CallSemanticSearch(args)
	if err != nil {
		return err
	}
	data, _ := res.(map[string]any)
	content, _ := data["content"].([]map[string]any)
	metadata, _ := data["metadata"].(map[string]any)
	
	for _, r := range content {
		_ = enc.Encode(map[string]any{"event": "item", "data": r})
		flusher.Flush()
	}
	_ = enc.Encode(map[string]any{"event": "item", "data": map[string]any{"metadata": metadata}})
	flusher.Flush()
	return nil
}
